package com.hwd.gmall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hwd.gmall.cart.feign.CartFeignClient;
import com.hwd.gmall.common.constant.Const;
import com.hwd.gmall.common.constant.OrderConst;
import com.hwd.gmall.common.constant.PaymentConst;
import com.hwd.gmall.common.util.MyThreadLocal;
import com.hwd.gmall.model.cart.CartInfo;
import com.hwd.gmall.model.enums.OrderStatus;
import com.hwd.gmall.model.enums.PaymentWay;
import com.hwd.gmall.model.enums.ProcessStatus;
import com.hwd.gmall.model.order.OrderDetail;
import com.hwd.gmall.model.order.OrderInfo;
import com.hwd.gmall.order.mapper.OrderDetailMapper;
import com.hwd.gmall.order.mapper.OrderInfoMapper;
import com.hwd.gmall.order.service.OrderService;
import com.hwd.gmall.payment.feign.WeiXinPayFeignClient;
import com.hwd.gmall.product.feign.ProductFeignClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 订单服务相关的接口的实现类类
 *
 * @author 黄伟东/Victor
 * @date 2022/5/7 20:08
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Resource
    private OrderDetailMapper orderDetailMapper;

    @Resource
    private CartFeignClient cartFeignClient;

    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private WeiXinPayFeignClient weiXinPayFeignClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RabbitTemplate.ConfirmCallback orderConfirmCallback;

    @Resource
    private RabbitTemplate.ReturnCallback orderReturnCallback;

    /**
     * 新增订单
     *
     * @param orderInfo 新增订单对象
     */
    @Override
    public void addOrder(OrderInfo orderInfo) {

        // 参数校验
        if (orderInfo == null) {
            return;
        }

        // 查询购物车微服务,获取本次要购买的完整的购物车数据和总金额总数量
        Map<String, Object> confirmedCartInfo = cartFeignClient.getOrderConfirmedCartInfo();
        if (confirmedCartInfo == null) {
            return;
        }
        // 获取用户名
        String username = MyThreadLocal.get();
        // 使用redis实现防止重复和多端下单
        Long increment = redisTemplate.opsForValue().increment(OrderConst.USER_CART_COUNT + username, 1);
        if (increment != null && increment > 1) {
            return;
        }

        try {

            // 补全oderInfo对象的信息
            Object totalMoney = confirmedCartInfo.get("totalMoney");
            orderInfo.setTotalAmount(new BigDecimal(totalMoney.toString()));
            orderInfo.setOrderStatus(OrderStatus.UNPAID.getComment());
            orderInfo.setUserId(username);
            orderInfo.setPaymentWay(PaymentWay.ONLINE.getComment());
            orderInfo.setCreateTime(new Date());
            orderInfo.setExpireTime(new Date(System.currentTimeMillis() + 30 * 60 * 1000));
            orderInfo.setProcessStatus(ProcessStatus.UNPAID.getComment());

            // 保存orderInfo的信息到数据库
            int insert = orderInfoMapper.insert(orderInfo);
            if (insert <= 0) {
                throw new RuntimeException("新增订单失败!");
            }

            // 保存orderInfo到数据库,保存完成以后,就有了订单号orderId----事务一(service-order)
            Long orderId = orderInfo.getId();

            // 声明扣减库存的对象
            Map<String, Object> stockDeductMap = new ConcurrentHashMap<>(16);

            // 包装订单详情对象
            List<Object> cartInfoList = (List<Object>) confirmedCartInfo.get("cartInfoList");
            cartInfoList.forEach(obj -> {
                // 序列化
                String jsonString = JSONObject.toJSONString(obj);
                // 反序列化
                CartInfo cartInfo = JSONObject.parseObject(jsonString, CartInfo.class);
                // 初始化订单详情
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderId(orderId);
                orderDetail.setSkuId(cartInfo.getSkuId());
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setImgUrl(cartInfo.getImgUrl());
                orderDetail.setOrderPrice(cartInfo.getSkuPrice().multiply(new BigDecimal(cartInfo.getSkuNum())));
                orderDetail.setSkuNum(cartInfo.getSkuNum());

                // 保存订单详情数据
                int insert1 = orderDetailMapper.insert(orderDetail);
                if (insert1 <= 0) {
                    throw new RuntimeException("新增订单失败!");
                }

                // 记录扣减的商品
                stockDeductMap.put(cartInfo.getSkuId().toString(), cartInfo.getSkuNum());
            });

            // 清空购物车
            if (!cartFeignClient.removeCartInfoAfterAddCartInfo()) {
                throw new RuntimeException("新增订单失败!");
            }
            // 扣减库存
            if (!productFeignClient.deductStock(stockDeductMap)) {
                throw new RuntimeException("新增订单失败!");
            }

            // 发送延迟消息，30分钟后处理未支付的订单
            rabbitTemplate.setConfirmCallback(orderConfirmCallback);
            rabbitTemplate.setReturnCallback(orderReturnCallback);
            rabbitTemplate.convertAndSend(OrderConst.ORDER_NORMAL_EXCHANGE, OrderConst.ORDER_NORMAL_ROUTING_KEY,
                    orderId + ":" + username);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("新增订单失败!");
        } finally {
            // 删除标识位
            redisTemplate.delete(OrderConst.USER_CART_COUNT + username);
        }

    }

    /**
     * 主动取消订单，逻辑取消，即修改订单状态
     *
     * @param orderId     订单id
     * @param orderStatus 订单状态
     */
    @Override
    public void cancelOrder(Long orderId, String orderStatus) {
        cancelOrder(orderId, orderStatus, MyThreadLocal.get());
    }

    /**
     * 根据订单号查询订单的详情的信息统计需要回滚的商品的库存信息,并进行回滚
     *
     * @param orderId 订单id
     */
    private void rollBackSkuStock(Long orderId) {
        // 获取订单的全部详情
        List<OrderDetail> orderDetails =
                orderDetailMapper.selectList(
                        new LambdaQueryWrapper<OrderDetail>()
                                .eq(OrderDetail::getOrderId, orderId));
        // 统计需要回滚的库存数据
        Map<String, Object> stockRollbackMap = new ConcurrentHashMap<>(16);
        orderDetails.forEach(orderDetail -> {
            // 记录回滚的商品
            stockRollbackMap.put(orderDetail.getSkuId() + "", orderDetail.getSkuNum());
        });

        // 远程调用回滚--TODO:优化为异步，使用mq--作业自己实现
        if (!productFeignClient.rollbackStock(stockRollbackMap)) {
            throw new RuntimeException("回滚库存失败!!");
        }
    }

    /**
     * 取消订单，逻辑取消，即修改订单状态
     *
     * @param orderId     订单id
     * @param orderStatus 订单状态
     * @param username    用户名
     */
    @Override
    public void cancelOrder(Long orderId, String orderStatus, String username) {
        // 参数检验
        if (orderId == null) {
            return;
        }

        // 查询订单，判断订单是否存在
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo == null || orderInfo.getId() == null) {
            return;
        }
        // 关闭交易
        Map<String, String> resultMap = weiXinPayFeignClient.closePay(orderId + "");
        if (resultMap == null ||
                !Const.SUCCESS.equals(resultMap.get(PaymentConst.RETURN_CODE)) ||
                !Const.SUCCESS.equals(resultMap.get(PaymentConst.RESULT_CODE))) {
            throw new RuntimeException("取消订单失败！");
        }

        // 判断订单的状态: 必须要订单的状态为:未支付才能取消!!!(前置状态进行幂等性校验)
        if (orderInfo.getProcessStatus().equals(OrderStatus.UNPAID.getComment())) {
            orderInfo.setOrderStatus(orderStatus);
            int update = orderInfoMapper.update(orderInfo,
                    new LambdaQueryWrapper<OrderInfo>()
                            .eq(OrderInfo::getId, orderId).eq(OrderInfo::getUserId, username));
            if (update <= 0) {
                throw new RuntimeException("取消订单失败！");
            }
            // 订单的库存扣减回滚!!
            rollBackSkuStock(orderId);
        }
    }

    /**
     * 根据支付结果修改订单的状态
     *
     * @param messageString 支付结果
     */
    @Override
    public void updateOrderStatus(String messageString) {
        // 支付结果反序列化
        Map<String, String> result = JSONObject.parseObject(messageString, Map.class);
        // 获取支付结果，修改订单的状态
        if (Const.SUCCESS.equals(result.get(PaymentConst.RETURN_CODE)) &&
                Const.SUCCESS.equals(result.get(PaymentConst.RESULT_CODE))) {
            // 获取订单号
            Long orderId = Long.parseLong(result.get("out_trade_no"));
            // 查询订单信息
            OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
            // 判断订单是否存在，幂等性校验
            if (orderInfo != null &&
                    orderInfo.getId() != null &&
                    orderInfo.getOrderStatus().equals(OrderStatus.UNPAID.getComment())) {
                // 更新订单的状态和进度状态
                orderInfo.setOrderStatus(OrderStatus.PAID.getComment());
                orderInfo.setProcessStatus(ProcessStatus.PAID.getComment());
                // 获取第三方交易的订单号，并更新
                orderInfo.setOutTradeNo(result.get("transaction_id"));
                // 设置第三方支付结果报文
                orderInfo.setTradeBody(messageString);
                // 更新订单
                int update = orderInfoMapper.updateById(orderInfo);
                if (update <= 0) {
                    throw new RuntimeException("修改订单的状态失败");
                }
            }

        }

    }
}
